package ccnu.agile.service.impl.business;

import ccnu.agile.bean.request.Request;
import ccnu.agile.bean.response.Response;
import ccnu.agile.command.verlocitygeneric.IGenericCommand;
import ccnu.agile.command.verlocitygeneric.VelocityGenericInvoker;
import ccnu.agile.config.velocity.VelocityConfig;
import ccnu.agile.constant.Business;
import ccnu.agile.constant.System;
import ccnu.agile.enums.DatePattern;
import ccnu.agile.service.IBusinessService;
import ccnu.agile.utils.LogUtils;
import org.apache.velocity.VelocityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.*;

/**
 * @Description: 自动生成表对应代码业务处理服务类
 * @Author: JreamY
 * @Date: 2021/11/12
 **/
@Service(System.ApiBusinessName.GENERIC_DB_TABLE)
@ConditionalOnBean(VelocityConfig.class)
public class GenericDbTableBusiness implements IBusinessService<Request, Response> {

    @Autowired
    private VelocityContext velocityContext;

    @Autowired
    private DataSource dataSource;

    @Autowired
    @Qualifier(System.GenericCommandBean.ENTITY)
    private IGenericCommand entityCmd;

    @Autowired
    @Qualifier(System.GenericCommandBean.MAPPER_INTER)
    private IGenericCommand mapperInterCmd;

    @Autowired
    @Qualifier(System.GenericCommandBean.MAPPER_XML)
    private IGenericCommand mapperXmlCmd;

    @Override
    public void doBusiness(Request request, Response response) {

        // 获取要生成的表名
        String tableName = (String) request.get("tableName");

        String generalSql = "select * from " + tableName;

        try {
            // 获取数据库连接
            Connection connection = dataSource.getConnection();
            LogUtils.info(this, "数据库连接成功");

            // 获取字段注释等基本信息
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet descResultSet = metaData.getColumns(
                    connection.getCatalog(), Business.SymbolStr.PERCENT, tableName, Business.SymbolStr.PERCENT
            );

            // 获取表字段长度
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(generalSql);
            ResultSetMetaData rsmd = resultSet.getMetaData();
            int columnCount = rsmd.getColumnCount();
            LogUtils.info(this, "数据表{}字段数为{}", tableName, columnCount);

            // 字段描述、字段类型、字段名称驼峰式、字段名称原值
            String[][] attrs = new String[columnCount][4];
            String[][] noIdAttrs = new String[columnCount - 1][4];
            for (int i = 0, j = 0; i < columnCount; i++) {

                descResultSet.next();

                String columnPropertyName = toCamel(descResultSet.getString("COLUMN_NAME"));
                String columnTypeName = toJavaType(descResultSet.getString("TYPE_NAME"));
                String columnName = descResultSet.getString("COLUMN_NAME").toUpperCase();

                attrs[i][0] = descResultSet.getString("REMARKS");
                attrs[i][1] = columnTypeName;
                attrs[i][2] = columnPropertyName;
                attrs[i][3] = columnName;

                if (attrs[i][2].equalsIgnoreCase("id")) {
                    continue;
                }

                noIdAttrs[j][0] = descResultSet.getString("REMARKS");
                noIdAttrs[j][1] = columnTypeName;
                noIdAttrs[j][2] = columnPropertyName;
                noIdAttrs[j++][3] = columnName;
            }

            // 关闭流资源
            resultSet.close();
            descResultSet.close();
            connection.close();
            LogUtils.info(this, "数据库资源释放");

            // 放置模板渲染数据
            String className = (String) request.get("className");
            velocityContext.put("className", className);
            velocityContext.put("description", request.get("description"));
            velocityContext.put("author", request.get("author"));
            velocityContext.put("date", DatePattern.Y_M_D_H_M_S.now());
            velocityContext.put("attrs", attrs);
            velocityContext.put("noIdAttrs", noIdAttrs);
            LogUtils.info(this, "Velocity模板渲染数据增加完成");

            // 模板生成
            new VelocityGenericInvoker(className, entityCmd)
                    .execute()
                    .update(mapperInterCmd)
                    .execute()
                    .update(mapperXmlCmd)
                    .execute();

        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    /**
     * role_id to roleId
     *
     * @param colName role_id
     * @return roleId
     */
    private String toCamel(String colName) {

        StringBuilder camelStrBuilder = new StringBuilder();
        int len = colName.length();
        boolean flag = false;

        StringBuilder lowerColName = new StringBuilder();
        for (int i = 0; i < len; i++) {
            char current = colName.charAt(i);
            if (current >= 'A' && current <= 'Z') {
                current += 32;
            }
            lowerColName.append(current);
        }
        colName = lowerColName.toString();


        for (int i = 0; i < len; i++) {
            char current = colName.charAt(i);
            if (Business.Symbol.UNDERLINE == current) {
                flag = true;
                continue;
            }
            if (flag) {
                flag = false;
                camelStrBuilder.append((char) (current - 32));
                continue;
            }
            camelStrBuilder.append(current);
        }
        return camelStrBuilder.toString();
    }

    /**
     * jdbcType to javaType
     *
     * @param dbType
     * @return
     */
    private String toJavaType(String dbType) {

        if (
                dbType.equalsIgnoreCase("INT")
                        || dbType.equalsIgnoreCase("TINYINT")
                        || dbType.equalsIgnoreCase("INTEGER")
                        || dbType.equalsIgnoreCase("NUMERIC")
        ) {
            return "Integer";
        }

        if (
                dbType.equalsIgnoreCase("VARCHAR")
                        || dbType.equalsIgnoreCase("CHAR")
        ) {
            return "String";
        }

        if (
                dbType.equalsIgnoreCase("DATETIME")
                        || dbType.equalsIgnoreCase("DATE")
                        || dbType.equalsIgnoreCase("TIMESTAMP")
        ) {
            return "Date";
        }

        // unknow type
        return dbType;
    }
}
